#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gctypes.h>
#include "gettext.h"

typedef struct _MSG {
	u32 id;
	char* msgstr;
	struct _MSG *next;
} MSG;
static MSG *baseMSG=0;


#define HASHWORDBITS 32

/* Defines the so called `hashpjw' function by P.J. Weinberger
   [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
   1986, 1987 Bell Telephone Laboratories, Inc.]  */
//static inline
u32 hash_string (const char *str_param)
{
	u32 hval, g;
	const char *str = str_param;

	/* Compute the hash value for the given string.  */
	hval = 0;
	while (*str != '\0') {
		hval <<= 4;
		hval += (u8) *str++;
		g = hval & ((u32) 0xf << (HASHWORDBITS - 4));
		if (g != 0) {
			hval ^= g >> (HASHWORDBITS - 8);
			hval ^= g;
		}
	}
	return hval;
}

// limit string length to max n chars
u32 hash_string_n (const char *str_param, int n)
{
	u32 hval, g;
	const char *str = str_param;

	/* Compute the hash value for the given string.  */
	hval = 0;
	while (n && *str != '\0') {
		hval <<= 4;
		hval += (u8) *str++;
		g = hval & ((u32) 0xf << (HASHWORDBITS - 4));
		if (g != 0) {
			hval ^= g >> (HASHWORDBITS - 8);
			hval ^= g;
		}
		n--;
	}
	return hval;
}

/* Expand some escape sequences found in the argument string.  */
static char *
expand_escape (const char *str) {
	char *retval, *rp;
	const char *cp = str;

	retval = (char *) malloc (strlen (str)+1);
	if (retval==NULL) return NULL;
	rp = retval;

	while (cp[0] != '\0' && cp[0] != '\\')
		*rp++ = *cp++;
	if (cp[0] == '\0')
		goto terminate;
	do {

		/* Here cp[0] == '\\'.  */
		switch (*++cp) {
			case '\"':		/* " */
				*rp++ = '\"';
				++cp;
				break;
			case 'a':		/* alert */
				*rp++ = '\a';
				++cp;
				break;
			case 'b':		/* backspace */
				*rp++ = '\b';
				++cp;
				break;
			case 'f':		/* form feed */
				*rp++ = '\f';
				++cp;
				break;
			case 'n':		/* new line */
				*rp++ = '\n';
				++cp;
				break;
			case 'r':		/* carriage return */
				*rp++ = '\r';
				++cp;
				break;
			case 't':		/* horizontal tab */
				*rp++ = '\t';
				++cp;
				break;
			case 'v':		/* vertical tab */
				*rp++ = '\v';
				++cp;
				break;
			case '\\':
				*rp = '\\';
				++cp;
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7': {
						  int ch = *cp++ - '0';

						  if (*cp >= '0' && *cp <= '7') {
							  ch *= 8;
							  ch += *cp++ - '0';

							  if (*cp >= '0' && *cp <= '7') {
								  ch *= 8;
								  ch += *cp++ - '0';
							  }
						  }
						  *rp = ch;
					  }
					  break;
			default:
					  *rp = '\\';
					  break;
		}

		while (cp[0] != '\0' && cp[0] != '\\')
			*rp++ = *cp++;
	} while (cp[0] != '\0');

	/* Terminate string.  */
terminate:
	*rp = '\0';
	return retval;
}

static MSG *findMSG(u32 id) {
	MSG *msg;
	for (msg=baseMSG; msg; msg=msg->next) {
		if (msg->id == id)
			return msg;
	}
	return NULL;
}

static MSG *setMSG(const char *msgid, const char *msgstr)
{
	if (!*msgstr) return NULL;
	char *tmp = expand_escape(msgid);
	u32 id = hash_string(tmp);
	free(tmp);
	MSG *msg = findMSG(id);
	if (!msg) {
		msg = (MSG *)malloc(sizeof(MSG));
		msg->id		= id;
		msg->msgstr = NULL;
		msg->next	= baseMSG;
		baseMSG		= msg;
	}
	if (msg) {
		if (msgstr) {
			if (msg->msgstr) free(msg->msgstr);
			//msg->msgstr = strdup(msgstr);
			msg->msgstr = expand_escape(msgstr);
		}
		return msg;
	}
	return NULL;
}

void gettextCleanUp(void) {
	while (baseMSG) {
		MSG *nextMsg =baseMSG->next;
		free(baseMSG->msgstr);
		free(baseMSG);
		baseMSG = nextMsg;
	}
}

char *str_cat_realloc(char *str, char *cat)
{
	char *newstr = malloc(strlen(str) + strlen(cat) + 1);
	strcpy(newstr, str);
	strcat(newstr, cat);
	free(str);
	return newstr;
}

bool gettextLoadLanguage(const char* langFile) {
	FILE *f;
	char line[200];
	char *lastID=NULL;
	char *lastSTR=NULL;
	char *str, *end;

	gettextCleanUp();
	f = fopen(langFile, "r");
	if (!f)
		return false;

	while (fgets(line, sizeof(line), f)) {
		// lines starting with # are comments
		//if (line[0] == '#')
		//    continue;
		if (strncmp(line, "msgid \"", 7) == 0) {
			char *msgid;
			if (lastSTR) {
				free(lastSTR);
				lastSTR=NULL;
			}
			if (lastID) {
				free(lastID);
				lastID=NULL;
			}
			msgid = &line[7];
			end = strrchr(msgid, '"');
			if (end && end >= msgid) {
				*end = 0;
				lastID = strdup(msgid);
			}
		} else if (line[0] == '"' && lastID && !lastSTR) {
			// multiline continuation of msgid
			str = &line[1];
			end = strrchr(str, '"');
			if (end && end >= str) {
				*end = 0;
				lastID = str_cat_realloc(lastID, str);
			}
		} else if (strncmp(line, "msgstr \"", 8) == 0) {
			if (lastID == NULL)
				continue;
			str = &line[8];
			end = strrchr(str, '"');
			if (end && end >= str) {
				*end = 0;
				lastSTR = strdup(str);
			}
		} else if (line[0] == '"' && lastID && lastSTR) {
			// multiline continuation of msgstr
			str = &line[1];
			end = strrchr(str, '"');
			if (end && end >= str) {
				*end = 0;
				lastSTR = str_cat_realloc(lastSTR, str);
			}
		} else if (lastID && lastSTR) {
			setMSG(lastID, lastSTR);
			free(lastID);
			free(lastSTR);
			lastID=NULL;
			lastSTR=NULL;
		}
	}
	// end of file, add last msg:
	if (lastID && lastSTR) {
		setMSG(lastID, lastSTR);
		free(lastID);
		free(lastSTR);
		lastID=NULL;
		lastSTR=NULL;
	}

	fclose(f);
	return true;
}

const char *gettext(const char *msgid)
{
	MSG *msg = findMSG(hash_string(msgid));
	if (msg && msg->msgstr) msgid = msg->msgstr;
	// skip ~~
	char *xx = strstr(msgid, "~~");
	if (xx) msgid = xx + 2;
	return msgid;
}

char** translate_array(int n, char *src[], char *dest[])
{
	int i;
	if (n && src && dest) {
		for (i=0; i<n; i++) {
			if (!src[i]) break;
			dest[i] = (char*)gettext(src[i]);
		}
	}
	return dest;
}


